#include "call_graph.h"

void printNode(function_node *current_node);

CallGraph::CallGraph(){
	size = 0;
	headFunction = NULL;
	current_func_node = NULL;
}

CallGraph::~CallGraph(){
	for(unsigned int i=0;i<functionList.size();i++){
		function_node* current = functionList[i];
		current->calleeList.clear();
		dr_close_file(current->file_function_cfg);
		delete current->cfg;
	}
	this->functionList.clear();
}

void CallGraph::setHeadFunction(function_node *entranceFunction){
	headFunction = entranceFunction;
}

function_node* CallGraph::getHeadFunction(){
	return headFunction;
}

function_node* CallGraph::createFunctionNode(unsigned int entrance_address,unsigned int return_address,char *node_name){
	for(unsigned int i=0;i<functionList.size();i++){
		function_node* current = functionList[i];
		if(current->entrance_address==entrance_address){
			return current;
		}
	}
	function_node *newFunction = new function_node;
	if(node_name!=NULL)
		strcpy(newFunction->function_name,node_name);

	newFunction->entrance_address = entrance_address;
	newFunction->expected_return_address = return_address;
	newFunction->cfg = new CFG;
	newFunction->file_function_cfg = NULL;
	functionList.push_back(newFunction);
	if(size==0){
		newFunction->deep = 0;
		newFunction->breadth = 0;
		headFunction = newFunction;
	}else{
		newFunction->deep = -1;
		newFunction->breadth = -1;
	}
	size++;
	return newFunction;
}


bool CallGraph::addCallee(function_node *caller, function_node *callee){
	unsigned int caller_address= caller->entrance_address;
	unsigned int callee_address= callee->entrance_address;
	bool hasCallee = false;
	bool hasCaller = false;
	for(unsigned int i=0;i<functionList.size();i++){
		
		function_node* current = functionList[i];
		if(current->entrance_address==caller_address)
			hasCaller = true;
		if(current->entrance_address == callee_address)
			hasCallee = true;
	}
	if(hasCallee&&hasCaller){
		callee->caller = caller;
		callee->deep = caller->deep+1;
		callee->breadth = caller->calleeList.size();
		caller->calleeList.push_back(callee);
		return true;
	}
	return false;
}

function_node* CallGraph::getCaller(function_node *callee){
	unsigned int callee_address= callee->entrance_address;
	for(unsigned int i=0;i<functionList.size();i++){
		function_node* current = functionList[i];
		if(current->entrance_address == callee_address)
			return callee->caller;
	}
	return NULL;
}

int CallGraph::getSize(){
	return size;
}

void CallGraph::printAllNode(){
	if(this->headFunction==NULL){
		return;
	}else{
		printNode(this->headFunction);
	}
}

void CallGraph::printSingleNode(function_node *current_node){
	if(current_node==NULL){
		return;
	}else{
		//////////////// create cfg file for the current node ////////////////
		if(current_node->file_function_cfg!=NULL){
			dr_close_file(current_node->file_function_cfg);
		}
		char logname[256];
		char tmp[256];
		//strcpy(logname,newFunction->function_name);
		//strcat(logname,".log\0");
		int len = dr_snprintf(logname, sizeof(logname)/sizeof(logname[0]),"%s", dr_get_client_path(client_id));
		char *dirsep;
		DR_ASSERT(len > 0);
		for (dirsep = logname + len; *dirsep != DIRSEP; dirsep--)
			DR_ASSERT(dirsep > logname);
		strcpy(tmp,"cfg/");
		strcat(tmp,current_node->function_name);
		strcat(tmp,".dot");
		len = dr_snprintf(dirsep + 1,
			(sizeof(logname) - (dirsep - logname))/sizeof(logname[0]),
			tmp);
		DR_ASSERT(len > 0);
		logname[sizeof(logname)/sizeof(logname[0])-1] = '\0';
		current_node->file_function_cfg = dr_open_file(logname,DR_FILE_WRITE_OVERWRITE);	
		//////////// take care that the cfg file is still open ///////////

		current_node->cfg->printCFG(current_node->file_function_cfg);	//print the cfg for current function
	}
}

void printNode(function_node *current_node){
	if(current_node==NULL){
		return;
	}else{
		//////////////// create cfg file for the current node ////////////////
		if(current_node->file_function_cfg!=NULL){
			dr_close_file(current_node->file_function_cfg);
		}
		char logname[256];
		char tmp[256];
		//strcpy(logname,newFunction->function_name);
		//strcat(logname,".log\0");
		int len = dr_snprintf(logname, sizeof(logname)/sizeof(logname[0]),"%s", dr_get_client_path(client_id));
		char *dirsep;
		DR_ASSERT(len > 0);
		for (dirsep = logname + len; *dirsep != DIRSEP; dirsep--)
			DR_ASSERT(dirsep > logname);
		strcpy(tmp,"cfg/");
		strcat(tmp,current_node->function_name);
		strcat(tmp,".dot");
		len = dr_snprintf(dirsep + 1,
			(sizeof(logname) - (dirsep - logname))/sizeof(logname[0]),
			tmp);
		DR_ASSERT(len > 0);
		logname[sizeof(logname)/sizeof(logname[0])-1] = '\0';
		current_node->file_function_cfg = dr_open_file(logname,DR_FILE_WRITE_OVERWRITE);
		//////////// take care that the cfg file is still open ///////////

		current_node->cfg->printCFG(current_node->file_function_cfg);	//print the cfg for current function
		for(int i=0;i<current_node->calleeList.size();i++)
		{
			printNode(current_node->calleeList[i]);	//print the cfg for each callee function
		}
	}
}
